<?php
require_once 'Zend/Loader.php';
require_once 'Zend/Loader/Autoloader.php';
$autoLoader = Zend_Loader_Autoloader::getInstance ();
require_once 'simpletest/unit_tester.php';
require_once 'simpletest/reporter.php';
/**
 * アクションコントローラ単体テストインターフェイス
 */
class ユニットテストケース extends UnitTestCase {
	public $テストクラスインスタンス;
	protected $テストするクラス名;
	protected $ゼンドリクエスト;
	protected $ゼンドレスポンス;

	public function __construct($label = false) {
		parent::__construct ( $label );
		require_once 'Zend/Controller/Request/Http.php';
		$this->ゼンドリクエスト = new Zend_Controller_Request_Http ();
		require_once 'Zend/Controller/Response/Http.php';
		$this->ゼンドレスポンス = new Zend_Controller_Response_Http ();
		runkit_method_add ( $this->テストするクラス名, $this->テストするクラス名 . '_getValue', '$name', 'return $this->$name;', RUNKIT_ACC_PUBLIC );
		if (method_exists($this->テストするクラス名, '_forward')) {
			runkit_method_remove ( $this->テストするクラス名, '_forward' );
			runkit_method_add (
			$this->テストするクラス名,
			'_forward',
			'$p1, $p2=FALSE, $p3=FALSE',
			'$module = "";$controller = str_replace("Controller", "", get_class());if($p2 === FALSE) {$action = $p1;} else if ($p3 === FALSE) {$action = $p1;$controller = $p2;} else {$action = $p1;$controller = $p2;$module = $p3;}$ret = "/";if ($module !=""){$ret .= $module . "/";}$this->リダイレクト先URL .= $ret . $controller . "/" . $action ."/";'
			);
		}
	}
	public function setUp()
	{
		global $ヘッダ送信文字;
		$ヘッダ送信文字 = array();
	}
	/**
	 * ZendActonControllerのインスタンスを返す
	 * @param string $actionName
	 * @return Zend_Controller_Action
	 */
	public function createAction($actionName = 'テストアクションラッピング') {
		$拡張クラス名 = 'ex_' . $this->テストするクラス名;
		/* 拡張クラス定義が読み込まれていない場合は読み込む */
		if (! class_exists ( $拡張クラス名 )) {
			$拡張クラス定義ファイル名 = アクション用拡張クラス作成 ( $拡張クラス名, $this->テストするクラス名 );
			require $拡張クラス定義ファイル名;
		}
		$action = new $拡張クラス名 ( $this->ゼンドリクエスト, $this->ゼンドレスポンス, array () );
		return $action;
	}
	/**
	 * テストクラスの不可視プロパティの値を返す
	 * @param $name プロパティ名
	 * @return unknown
	 */
	protected function getValue($name) {
		$ret = $this->テストクラスインスタンス->{$this->テストするクラス名 . '_getValue'} ( $name );
		return $ret;
	}
	/**
	 * 不可視メソッドを実行
	 * @param string $methodName
	 * @param mixed $p1,,,
	 *        	引数（可変5個まで）
	 */
	protected function 強行($強行メソッド名, $p1 = NULL, $p2 = NULL, $p3 = NULL, $p4 = NULL, $p5 = NULL) {
		$methodA = new ReflectionMethod ( $this->テストクラスインスタンス, $強行メソッド名 );
		$methodA->setAccessible ( true );
		if ($p5 !== NULL) {
			$result = $methodA->invoke ( $this->テストクラスインスタンス, $p1, $p2, $p3, $p4, $p5 );
		} else if ($p4 !== NULL) {
			$result = $methodA->invoke ( $this->テストクラスインスタンス, $p1, $p2, $p3, $p4 );
		} else if ($p3 !== NULL) {
			$result = $methodA->invoke ( $this->テストクラスインスタンス, $p1, $p2, $p3 );
		} else if ($p2 !== NULL) {
			$result = $methodA->invoke ( $this->テストクラスインスタンス, $p1, $p2 );
		} else if ($p1 !== NULL) {
			$result = $methodA->invoke ( $this->テストクラスインスタンス, $p1 );
		} else {
			$result = $methodA->invoke ( $this->テストクラスインスタンス );
		}
		return $result;
	}
	/**
	 * クラスメソッドをモッククラスメソッドに差し替える
	 * @param $オリジナルクラスメソッド名 クラス名::メソッド名
	 * @param $モッククラスメソッド名 クラス名::メソッド名
	 * @return boolean
	 */
	protected function クラスメソッドモック化($オリジナルクラスメソッド名, $モッククラスメソッド名) {
		$返却値 = TRUE;
		$オリジナルクラスメソッド名分割 = explode ( '::', $オリジナルクラスメソッド名 );
		/* フォーマット判定 */
		if (count ( $オリジナルクラスメソッド名分割 ) != 2) {
			return FALSE;
		}
		$モッククラスメソッド名分割 = explode ( '::', $モッククラスメソッド名 );
		/* フォーマット判定 */
		if (count ( $モッククラスメソッド名分割 ) != 2) {
			return FALSE;
		}
		$返却値 = $this->_switchClassMethod ( $オリジナルクラスメソッド名分割 [0], $オリジナルクラスメソッド名分割 [1], $モッククラスメソッド名分割 [0], $モッククラスメソッド名分割 [1] );
		return $返却値;
	}
	/**
	 * 外部クラスのメソッドを替え玉にすり替える
	 * @param $className 外部クラス名
	 * @param $methodName 外部クラスのすり替え対象メソッド名
	 * @param $destClassName 替え玉クラス名
	 * @param $destMethodName 替え玉メソッド名
	 */
	protected function _switchClassMethod($className, $methodName, $destClassName, $destMethodName) {
		/* まずは対象の外部クラスをしっかり調査 */
		$classReference = new ReflectionClass ( $className );
		/* すり替え対象の外部クラスにすり替えるメソッドが定義済みでない場合はすり替えではなく追加にする */
		if ($classReference->hasMethod ( $methodName )) {
			runkit_method_remove ( $className, $methodName );
		}
		runkit_method_copy ( $className, $methodName, $destClassName, $destMethodName );
	}
}
/**
 * exitを無効化する
 */
function exit抹消() {
	uopz_overload ( ZEND_EXIT, function () {
	} );
}
/**
 * 無効化したexitを元に戻す
 */
function exit復活() {
	uopz_overload ( ZEND_EXIT, null );
}
$ヘッダ送信文字 = '';
uopz_backup ( "header" );
uopz_function ( "header", function ($s) {
	global $ヘッダ送信文字;
	$ヘッダ送信文字[] = $s;
} );
/**
 *
 * @var time関数の返却値を前後にずらす秒を設定するグローバル変数
 */
$time関数モック化フラグ = FALSE;
$ずらす秒数 = 0;
function time関数をモック化() {
	GLOBAL $time関数モック化フラグ;
	/**
	 * *モック化済みの場合は何もしない
	 */
	if ($time関数モック化フラグ === TRUE) {
		return;
	}
	$time関数モック化フラグ = TRUE;
	uopz_rename ( "time", "orgTime" );
	uopz_function ( "time", function () {
		GLOBAL $ずらす秒数;
		$ret = orgTime () + $ずらす秒数;
		return $ret;
	} );
}
function モック化したtime関数を戻す() {
	GLOBAL $time関数モック化フラグ;
	$time関数モック化フラグ = FALSE;
	uopz_rename ( "orgTime", "time" );
	uopz_delete ( "orgTime" );
}
/**
 * アクション用拡張クラス定義ファイルを動的に作成する
 * @param unknown $拡張クラス名
 * @param unknown $テストするクラス名
 * @return string
 */
function アクション用拡張クラス作成($拡張クラス名, $テストするクラス名)
{
	$拡張クラス定義ファイル名 = dirname(__FILE__).'/SimpletestExtClass.php';
	/* すでに拡張クラス定義が読み子もあれている場合は即リターン*/
	if (class_exists($拡張クラス名)){
		return $拡張クラス定義ファイル名;
	}
	/* 拡張クラス定義ファイルが未作成の場合、作成する*/
	if(!file_exists($拡張クラス定義ファイル名)){
		file_put_contents($拡張クラス定義ファイル名, "<?php\n");
	}
	$入力テキスト = file_get_contents($拡張クラス定義ファイル名);
	/* 拡張クラス定義ファイルに拡張クラス定義済みであれば、即リターン*/
	if (mb_strpos($入力テキスト,sprintf('class %s extends',$拡張クラス名))!==FALSE){
		return $拡張クラス定義ファイル名;
	}
	$入力テキスト .= "\n" . sprintf(EXTENSION_CLASS_TEMPLATE, $拡張クラス名, $テストするクラス名, $拡張クラス名, $テストするクラス名);
	file_put_contents($拡張クラス定義ファイル名, $入力テキスト);
	return $拡張クラス定義ファイル名;
}
define('EXTENSION_CLASS_TEMPLATE',
<<<EOD
if (!class_exists('%s')){
    if (class_exists('%s')){
        class %s extends %s{
             public \$リダイレクト先URL;
             public function init()
             {
                   /** CLI起動はviewのインスタンスがないので自前で創る  */
                   if (! isset ( \$this->view )) {
                      \$this->view = new stdClass ();
                  }
                  parent::init ();
             }
             public function _redirect(\$url)
             {
                   \$this->リダイレクト先URL = \$url;
             }
        }
    }
}
EOD
);
define ( 'PRELOAD_DIR', __DIR__ );